package swings.mycomponent.progressmonitor;

import static java.lang.System.out;

import java.awt.Color;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingWorker;

import net.miginfocom.swing.MigLayout;

import org.apache.log4j.Logger;

/**
 * @Title: SwingWorkerExampleJPanel.java
 * @Description: TODO(结合 JProgressBar，演示 SwingWorker 的用法。具体例子参见 PDF 项目)
 * @author H819
 * @date 2011-2-24
 * @version V1.0
 */
public class SwingWorkerExampleJPanel extends JPanel implements
		PropertyChangeListener {

	// 都列为全局变量，便于在下面的方法中都能调用。
	private JLabel announceLable;
	private JLabel messageLabel;
	private JTextField textField;
	private JProgressBar progressBar;
	// ProgressMonitor 不好操控，不用这个组件
	// private ProgressMonitor progressMonitor;
	private JButton buttonStart;
	private JButton buttonCancel;

	// 提示信息
	private String message = "";
	private Task task;
	private int alltasknum =0;

	private static Logger log = Logger
			.getLogger(SwingWorkerExampleJPanel.class);

	/**
	 * Create the panel.
	 */
	public SwingWorkerExampleJPanel() {

		setLayout(new MigLayout("", "[grow]", "[][][][][][][][]"));

		announceLable = new JLabel("演示：组件随线程更新,必须用 SwingWorker.");
		add(announceLable, "cell 0 0");

		messageLabel = new JLabel("label");
		add(messageLabel, "cell 0 1");

		textField = new JTextField();
		add(textField, "cell 0 2,growx");
		textField.setColumns(10);

		progressBar = new JProgressBar(0, 100);
		progressBar.setStringPainted(true);
		progressBar.setBackground(Color.orange);
		progressBar.setForeground(Color.red);
		add(progressBar, "cell 0 3");

		buttonStart = new JButton("Start");

		buttonStart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {

				progressBar.setValue(0);

				// start
				task = new Task();
				// 为 task 添加监听器，该监听器实现方法是下文的 propertyChange 方法。
				task.addPropertyChangeListener(SwingWorkerExampleJPanel.this);
				buttonStart.setEnabled(false);

				task.execute();

				log.info("start");

			}
		});

		add(buttonStart, "flowx,cell 0 7");

		buttonCancel = new JButton("Cancel");
		buttonCancel.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {

				// cancel
				task.cancel(true);

			}
		});

		add(buttonCancel, "cell 0 7");

	}

	/**
	 * 执行任务的内部类，想要执行的操作，在这里执行。
	 * 
	 * 在这个类中，仅需要处理任务一、二、三、四、五即可，其他的代码不要改动.
	 * 
	 * 任务四在 done()方法中，可以最后做一些写入文件或资源清理的工作。
	 * 
	 * @ClassName: Task
	 * @Description: TODO(执行任务的内部类，想要执行的操作，在这里执行。)
	 * @author h819
	 * @date 2010-10-6 下午10:31:31
	 * @version V1.0
	 */

	class Task extends SwingWorker<Void, Void> {

		@Override
		// 执行任务的方法 ，当 progress =100 时，该方法自动停止执行
		public Void doInBackground() {
			Random random = new Random();

			/**
			 * progress 是 SwingWorker 默认的约束属性，下文的
			 * propertyChange(PropertyChangeEvent evt)也是 SwingWorker
			 * 中的默认方法，该方法跟踪 progress 变化。
			 * 
			 * progress 变量是一个从0到100的整型值。
			 * 
			 * progress 的变化通过 setProgress(0)体现。
			 * 
			 * 创建一个临时变量，模拟 SwingWorker 默认的约束属性 progress，为 int 类型
			 * 
			 * 使其变化范围为 [0,100]，每次增长步长为 1 即在 100 次的变化范围内，完成任务
			 * 
			 * 所以，主要是寻找 progress 的增长时机
			 */

			int progress = 0;
			setProgress(0);

			/**
			 * 需要处理的任务一：开始
			 * 
			 * 根据具体情况求得任务总数
			 * */
			int count = 2300;

			/**
			 * 需要处理的任务一：完成
			 * */

			alltasknum = count;
			
			/**
			 * 需要处理的任务二：开始
			 * */
			// 其他必要的前期准备工作，不要放在 while 循环中处理的，在此处实现。

			/**
			 * 需要处理的任务二：完成
			 * */

			// progressBar 增长步长.
			// 因为总量为 100%,增长 100 次，达到总数。
			float countTofloat = count * 1.0f;
			int step = Math.round(countTofloat / 100f);
			// 开始时机
			int i = 1;

			// 怎么设计一个过程，是 progress 在 100 次的增长过程中，完成
			// 所有的任务。

			// 只有 i < count 条件和任务相关。不同的任务，设置不同的停止条件即可
			while (progress < 100 && !isCancelled() && i <= count) {

				// 如果处理的任务耗时很短，就加上一段延迟的代码，便于看清楚执行过程。
				try {
					Thread.sleep(random.nextInt(50));

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				// 已经到达任务总数，而 progress 还没到 100，直接赋值 progress 为 100，程序结束运行
				// 保证当处理程序中有一些 contine 语句直接回到 while 判断时，progress 能被赋值
				if (i == count) {
					progress = 100;
					out.println(" **************** update progress = "
							+ progress);

					// 更新参数
					setProgress(progress);
				}

				/**
				 * 
				 * 需要处理的任务三：开始
				 * 
				 * 仅在这上面处理任务即可
				 * 
				 * 需要放在 while 循环中逐条处理的任务
				 * 
				 * 循环 i，直至 i=count，任务才算完成
				 * 
				 * 有异常情况判断时，不要用 coninue
				 * 
				 * coninue 会跳过下面的 progress 增长语句. 用 if else 来实现异常情况的判断。
				 **/

				// 需要放在 while 循环中逐条处理的任务

				out.println("doSomething(): i = " + i);

				// 返回提示信息
				message = "任务总数是 " + count + "，进行到第   " + i + "  个任务";

				/**
				 * 
				 * 仅在这上面处理任务即可
				 * 
				 * 需要处理的任务三：结束
				 * 
				 **/

				/**
				 * 下面这段代码不要改动，仅是为了更新 progrss 用
				 * 
				 * */

				// 根据 i 的变化，分不同的情况，更新 progress ，和具体的业务没有关系
				// 为了进度条显示的更加合理，把任务数量分成了几个区间。
				// 否则:
				// 当任务很少的时候，进度条没有到 100%，任务已经完成了。
				// 当任务很多的时候，进度条已经进行到 到 100%，但任务还没有完成。
				if (count <= 100) {
					// 总数小于 100 ，progress 增长设计
					int divisor = 100 / count;

					if (i * divisor <= 100 && i <= count) {
						progress = i * divisor;

						if (i == count) {
							progress = 100;
							out.println(" **************** update progress = "
									+ progress);
						}

						// 更新参数
						setProgress(progress);
					}

				} else if (100 < count) {
					// 总数大于 100 ，progress 增长设计

					// 如果 progress 已经接近 100 ，但任务总数还没完成，则不更新 progress，继续处理任务。
					if (progress == 98 && i < count) {
						i++;
						continue;
					}

					if (i % step == 0) {// progress 增长 1
										// 的时机，经过四舍五入 Math.round
						progress++;
						out.println(" **************** update progress = "
								+ progress);
					}

					// 更新参数
					setProgress(progress);

				}

				i++;

				/**
				 * 上面这段代码不要改动，仅是为了更新 progrss 用
				 * 
				 * */

			}

			return null;
		}

		/**
		 * 任务完成之后会自动执行的动作
		 * 
		 * 此处可以做一些程序完成之后要做的写入文件或清理资源工作
		 * 
		 */
		@Override
		public void done() {
			Toolkit.getDefaultToolkit().beep();
			log.info("task done.");

			/**
			 * 需要处理的任务四：开始
			 **/

			buttonStart.setEnabled(true);

			/**
			 * 需要处理的任务四：结束
			 **/

		}

	}

	/**
	 * 跟踪 Task 类中 progress 变量变化，当 progress 变量 变化时，直接触发这个方法，更新组件信息。
	 * 
	 * progress 变化范围为[0,100]
	 * 
	 */
	public void propertyChange(PropertyChangeEvent evt) {
		if ("progress" == evt.getPropertyName()) {

			// 下面这个 progress ，数值取自 Task 类中的默认变量 progress
			int progress = (Integer) evt.getNewValue();

			// taskOutput.append(message);
			if (task.isCancelled()) {
				log.info("task isCancelled()");
				buttonStart.setEnabled(true);
			}
			

			
			/**
			 * 需要处理的任务五: 开始
			 * 
			 * 此处可以在所有的任务结束之后，做一些相关的处理
			 **/
			
			// 返回提示信息
			if (progress == 100)
				message = "任务完成,共  "+alltasknum+" 个";
			
			/**
			 * 需要处理的任务五: 结束
			 **/
			

			// lable 信息更新
			messageLabel.setText(this.message);
			// textField 信息更新
			textField.setText(this.message);
			// progressBar 信息更新

			progressBar.setValue(progress);

		}

	}

}
